| 80386响应 中断/异常 的优先级  | 
中断/异常类型 | 优先级 | 
| 调试故障 | 最高 | |
| 其它故障 | ↓ | |
| 陷阱指令INT n和INTO | ↓ | |
| 调试陷阱 | ↓ | |
| NMI中断 | ↓ | |
| INTR中断 | 最低 | 
| 
异 常 一 览 表  | 
向量号 | 异常名称 | 异常类型 | 出错代码 | 相关指令 | 
| 0 | 除法出错 | 故障 | 无 | DIV,IDIV | |
| 1 | 调试异常 | 故障/陷阱 | 无 | 任何指令 | |
| 3 | 单字节INT3 | 陷阱 | 无 | INT 3 | |
| 4 | 溢出 | 陷阱 | 无 | INTO | |
| 5 | 边界检查 | 故障 | 无 | BOUNT | |
| 6 | 非法操作码 | 故障 | 无 | 非法指令编码或操作数 | |
| 7 | 设备不可用 | 故障 | 无 | 浮点指令或WAIT | |
| 8 | 双重故障 | 中止 | 有 | 任何指令 | |
| 9 | 协处理器段越界 | 中止 | 无 | 访问存储器的浮点指令 | |
| 0AH | 无效TSS异常 | 故障 | 有 | JMP、CALL、IRET或中断 | |
| 0BH | 段不存在 | 故障 | 有 | 装载段寄存器的指令 | |
| 0CH | 堆栈段异常 | 故障 | 有 | 装载SS寄存器的任何指令、对SS寻址的段访问的任何指令 | |
| 0DH | 通用保护异常 | 故障 | 有 | 任何特权指令、任何访问存储器的指令 | |
| 0EH | 页异常 | 故障 | 有 | 任何访问存储器的指令 | |
| 10H | 协处理器出错 | 故障 | 无 | 浮点指令或WAIT | |
| 11H—0FFH | 软中断 | 陷阱 | 无 | INT n | 
| 出错代码的格式 | |||
| BIT15—BIT3 | BIT2 | BIT1 | BIT0 | 
| 选择子的索引部分 | TI | IDT | EXT | 

;名称:ASM6.ASM
;功能:演示中断处理的实现
;编译:TASM ASM6.ASM
;连接:TLINK ASM6.OBJ
;----------------------------------------------------------------------------
INCLUDE         386SCD.INC
;----------------------------------------------------------------------------
;部分常量定义
;----------------------------------------------------------------------------
EOICOM          =       20h                       ;外部中断处理结束命令
ICREGP          =       20h                       ;中断控制寄存器端口地址
IMREGP          =       21h                       ;中断屏蔽寄存器端口地址
;----------------------------------------------------------------------------
GDTSeg          SEGMENT PARA USE16                ;全局描述符表数据段(16位)
;----------------------------------------------------------------------------
                ;全局描述符表GDT
GDT             LABEL   BYTE
                ;空描述符
DUMMY           Desc    <>
                ;规范段描述符
Normal          Desc    <0ffffh,,,ATDW,,>
                ;视频缓冲区段描述符(DPL=3)
VideoBuf        Desc    <0ffffh,8000h,0bh,ATDW,,>
;----------------------------------------------------------------------------
EFFGDT          LABEL   BYTE
                ;临时代码段描述符
TempCode        Desc    <0ffffh,TempCodeSeg,,ATCE,,>
                ;演示代码段描述符
DemoCode        Desc    <DemoCodeLen-1,DemoCodeSeg,,ATCE,,>
                ;演示数据段描述符
DemoData        Desc    <DemoDataLen-1,DemoDataSeg,,ATDW,,>
                ;演示堆栈段描述符
DemoStack       Desc    <DemoStackLen-1,DemoStackSeg,,ATDWA,,>
                ;0feh号中断处理程序(显示程序)代码段描述符
EchoCode        Desc    <EchoCodeLen-1,EchoCodeSeg,,ATCE,,>
                ;0feh号中断处理程序(显示程序)数据段描述符
EchoData        Desc    <EchoDataLen-1,EchoDataSeg,,ATDW,,>
                ;8号中断处理程序代码段描述符
TICode          Desc    <TICodeLen-1,TICodeSeg,,ATCE,,>
                ;8号中断处理程序数据段描述符
TIData          Desc    <TIDataLen-1,TIDataSeg,,ATDW,,>
                ;其它中断或异常处理程序代码段描述符
Other           Desc    <OtherCodeLen-1,OtherCodeSeg,,ATCE,,>
;----------------------------------------------------------------------------
GDTLen          =       $-GDT                     ;全局描述符表长度
GDNum           =       ($-EFFGDT)/(SIZE Desc)    ;需特殊处理的描述符数
;----------------------------------------------------------------------------
Normal_Sel      =       Normal-GDT                ;规范段描述符选择子
Video_Sel       =       VideoBuf-GDT              ;视频缓冲区段描述符选择子
;----------------------------------------------------------------------------
TempCode_Sel    =       TempCode-GDT              ;临时代码段的选择子
DemoCode_Sel    =       DemoCode-GDT              ;演示代码段的选择子
DemoData_Sel    =       DemoData-GDT              ;演示数据段的选择子
DemoStack_Sel   =       DemoStack-GDT             ;演示堆栈段的选择子
EchoCode_Sel    =       EchoCode-GDT              ;0feh号中断程序代码段选择子
EchoData_Sel    =       EchoData-GDT              ;0feh号中断程序数据段选择子
TICode_Sel      =       TICode-GDT                ;8号中断程序代码段选择子
TIData_Sel      =       TIData-GDT                ;8号中断程序数据段选择子
Other_Sel       =       Other-GDT                 ;其它中断或异常代码段选择子
;----------------------------------------------------------------------------
GDTSeg          ENDS                              ;全局描述符表段定义结束
;----------------------------------------------------------------------------
IDTSeg          SEGMENT PARA USE16                ;中断描述符表数据段(16位)
;----------------------------------------------------------------------------
IDT             LABEL   BYTE                      ;中断描述符表
                ;0--7的8个陷阱门描述符
                REPT    8
                Gate    <OtherBegin,Other_Sel,,AT386TGate,>
                ENDM
                ;对应8号(时钟)中断处理程序的门描述符
                Gate    <TIBegin,TICode_Sel,,AT386IGate,>
                ;从9--0fdh的245个陷阱门描述符
                REPT    245
                Gate    <OtherBegin,Other_Sel,,AT386TGate,>
                ENDM
                ;对应0feh号中断处理程序的陷阱门描述符
                Gate    <EchoBegin,EchoCode_Sel,,AT386TGate,>
                ;对应0ffh号中断处理程序的陷阱门描述符
                Gate    <OtherBegin,Other_Sel,,AT386TGate,>
;----------------------------------------------------------------------------
IDTLen          =       $-IDT
;----------------------------------------------------------------------------
IDTSeg          ENDS                              ;中断描述符表段定义结束
;----------------------------------------------------------------------------
;其它中断或异常处理程序的代码段
;----------------------------------------------------------------------------
OtherCodeSeg    SEGMENT PARA USE16
                ASSUME  CS:OtherCodeSeg
;----------------------------------------------------------------------------
OtherBegin      PROC    FAR
                mov     ax,Video_Sel
                mov     es,ax
                mov     ah,17h                    ;在屏幕左上角显示兰底白字
                mov     al,'!'                    ;符号"!"
                mov     WORD PTR es:[0],ax
                jmp     $                         ;无限循环
OtherBegin      ENDP
;----------------------------------------------------------------------------
OtherCodeLen    =       $
OtherCodeSeg    ENDS
;----------------------------------------------------------------------------
;8号中断处理程序的数据段
;----------------------------------------------------------------------------
TIDataSeg       SEGMENT PARA USE16
Count           DB      0                         ;中断发生的计数器
TIDataLen       =       $
TIDataSeg       ENDS
;----------------------------------------------------------------------------
;8号中断处理程序的代码段
;----------------------------------------------------------------------------
TICodeSeg       SEGMENT PARA USE16
                ASSUME  CS:TICodeSeg,DS:TIDataSeg
;----------------------------------------------------------------------------
TIBegin         PROC    FAR
                push    eax                       ;保护现场
                push    ds
                push    fs
                push    gs
                mov     ax,TIData_Sel             ;置中断处理程序数据段
                mov     ds,ax
                mov     ax,EchoData_Sel           ;置显示过程数据段
                mov     fs,ax
                mov     ax,DemoData_Sel           ;置演示程序数据段
                mov     gs,ax
                cmp     Count,0
                jnz     TI2                       ;计数非0表示未到1秒
                mov     Count,18                  ;每秒约18次
                int     0feh                      ;调用0FEH号中断处理程序显示
                cmp     BYTE PTR fs:Mess,'0'
                jnz     TI1
                mov     BYTE PTR gs:Flag,1        ;显示符号'0'时置标记
TI1:            dec     BYTE PTR fs:Mess          ;调整显示符号
TI2:            dec     Count                     ;调整计数
                pop     gs                        ;恢复现场
                pop     fs
                pop     ds
                mov     al,EOICOM                 ;通知中断控制器中断处理结束
                out     ICREGP,al
                pop     eax
                iretd                             ;中断返回
TIBegin         ENDP
;----------------------------------------------------------------------------
TICodeLen       =       $
TICodeSeg       ENDS
;----------------------------------------------------------------------------
;0FEH号中断处理程序数据段
;----------------------------------------------------------------------------
EchoDataSeg     SEGMENT PARA USE16
Mess            DB      '8',4eh
EchoDataLen     =       $
EchoDataSeg     ENDS
;----------------------------------------------------------------------------
;0FEH号中断处理程序(显示程序)的代码段
;----------------------------------------------------------------------------
EchoCodeSeg     SEGMENT PARA USE16
                ASSUME  CS:EchoCodeSeg,DS:EchoDataSeg
;----------------------------------------------------------------------------
EchoBegin       PROC    FAR
                push    ax                        ;保护现场
                push    ds
                push    es
                mov     ax,EchoData_Sel           ;置显示过程数据段
                mov     ds,ax
                mov     ax,Video_Sel              ;置视频缓冲区数据段
                mov     es,ax
                mov     ax,WORD PTR Mess
                mov     WORD PTR es:[0],ax
                pop     es
                pop     ds
                pop     ax
                iretd
EchoBegin       ENDP
;----------------------------------------------------------------------------
EchoCodeLen     =       $
EchoCodeSeg     ENDS
;----------------------------------------------------------------------------
;演示任务的堆栈段
;----------------------------------------------------------------------------
DemoStackSeg    SEGMENT PARA USE16
DemoStackLen    =       1024
                DB      DemoStackLen DUP(0)
DemoStackSeg    ENDS
;----------------------------------------------------------------------------
;演示任务的数据段
;----------------------------------------------------------------------------
DemoDataSeg     SEGMENT PARA USE16
Flag            DB      0
DemoDataLen     =       $
DemoDataSeg     ENDS
;----------------------------------------------------------------------------
;演示任务的代码段
;----------------------------------------------------------------------------
DemoCodeSeg     SEGMENT PARA USE16
                ASSUME  CS:DemoCodeSeg,DS:DemoDataSeg
;----------------------------------------------------------------------------
DemoBegin       PROC    FAR
                mov     ax,DemoStack_Sel          ;置堆栈
                mov     ss,ax
                mov     sp,DemoStackLen           ;置数据段
                mov     ax,DemoData_Sel
                mov     ds,ax
                mov     es,ax
                mov     fs,ax
                mov     gs,ax
                mov     al,11111110b              ;置中断屏蔽字
                out     IMREGP,al                 ;只开发时钟中断
                sti                               ;开中断
DemoConti:      cmp     BYTE PTR Flag,0           ;判标志
                jz      DemoConti                 ;直到不为0
                cli                               ;关中断
                ;转回临时代码段,准备回实方式
                JUMP16  TempCode_Sel,<OFFSET ToDos>
DemoBegin       ENDP
;----------------------------------------------------------------------------
DemoCodeLen     =       $
DemoCodeSeg     ENDS
;----------------------------------------------------------------------------
TempCodeSeg     SEGMENT PARA USE16                ;临时任务的代码段
                ASSUME  CS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual         PROC    FAR
                JUMP16  DemoCode_Sel,DemoBegin    ;转演示任务
ToDos:          mov     ax,Normal_Sel             ;恢复实方式段描述符高速缓存
                mov     ds,ax
                mov     es,ax
                mov     fs,ax
                mov     gs,ax
                mov     ss,ax
                mov     eax,cr0                   ;准备返回实模式
                and     al,11111110b
                mov     cr0,eax
                JUMP16  <SEG Real>,<OFFSET Real>
Virtual         ENDP
;----------------------------------------------------------------------------
TempCodeSeg     ENDS
;============================================================================
RDataSeg        SEGMENT PARA USE16                ;实方式数据段
VGDTR           PDesc   <GDTLen-1,>               ;GDT伪描述符
VIDTR           PDesc   <IDTLen-1,>               ;IDT伪描述符
NORVIDTR        PDesc   <3ffh,>                   ;用于保存原IDTR值
SPVar           DW      ?                         ;用于保存实方式下的SP
SSVar           DW      ?                         ;用于保存实方式下的SS
IMaskRegV       DB      ?                         ;用于保存原中断屏蔽寄存器值
RDataSeg        ENDS
;----------------------------------------------------------------------------
RCodeSeg        SEGMENT PARA USE16                ;实方式代码段
                ASSUME  CS:RCodeSeg,DS:RDataSeg
;----------------------------------------------------------------------------
Start           PROC
                mov     ax,RDataSeg
                mov     ds,ax
                cld
                call    InitGDT                   ;初始化全局描述符表GDT
                call    InitIDT                   ;初始化中断描述符表IDT
                mov     SSVar,ss                  ;保存堆栈指针
                mov     SPVar,sp
                sidt    QWORD PTR NORVIDTR        ;保存IDTR
                in      al,IMREGP
                mov     BYTE PTR IMaskRegV,al
                lgdt    QWORD PTR VGDTR           ;装载GDTR
                cli                               ;关中断
                lidt    QWORD PTR VIDTR           ;装载IDTR
                mov     eax,cr0
                or      al,1
                mov     cr0,eax
                JUMP16  <TempCode_Sel>,<OFFSET Virtual>
Real:           mov     ax,RDataSeg
                mov     ds,ax
                lss     sp,DWORD PTR SPVar        ;又回到实方式
                lidt    QWORD PTR NORVIDTR
                mov     al,IMaskRegV
                out     IMREGP,al
                sti
                mov     ax,4c00h
                int     21h
Start           ENDP
;----------------------------------------------------------------------------
InitGDT         PROC
                push    ds
                mov     ax,GDTSeg
                mov     ds,ax
                mov     cx,GDNum
                mov     si,OFFSET EFFGDT
InitG:          mov     ax,[si].BaseL
                movzx   eax,ax
                shl     eax,4
                shld    edx,eax,16
                mov     WORD PTR [si].BaseL,ax
                mov     BYTE PTR [si].BaseM,dl
                mov     BYTE PTR [si].BaseH,dh
                add     si,SIZE Desc
                loop    InitG
                pop     ds
                mov     bx,16
                mov     ax,GDTSeg
                mul     bx
                mov     WORD PTR VGDTR.Base,ax
                mov     WORD PTR VGDTR.Base+2,dx
                ret
InitGDT         ENDP
;----------------------------------------------------------------------------
InitIDT         PROC
                mov     bx,16
                mov     ax,IDTSeg
                mul     bx
                mov     WORD PTR VIDTR.Base,ax
                mov     WORD PTR VIDTR.Base+2,dx
                ret
InitIDT         ENDP
;----------------------------------------------------------------------------
RCodeSeg        ENDS
                END     Start
    sidt    QWORD PTR NORVIDTR
    lidt    QWORD PTR VIDTR
    lidt    QWORD PTR NORVIDTR
;名称:ASM7.ASM
;功能:模拟异常和演示异常处理
;编译:TASM ASM7.ASM
;连接:TLINK ASM7.OBJ
;----------------------------------------------------------------------------
INCLUDE         386SCD.INC
;----------------------------------------------------------------------------
GDTSeg          SEGMENT PARA USE16                ;全局描述符表数据段(16位)
;----------------------------------------------------------------------------
                ;全局描述符表GDT
GDT             LABEL   BYTE
                ;空描述符
DUMMY           Desc    <>
                ;规范段描述符及选择子
Normal          Desc    <0ffffh,,,ATDW,,>
Normal_Sel      =       Normal-GDT
                ;视频缓冲区段描述符(DPL=3)及选择子
VideoBuf        Desc    <0ffffh,8000h,0bh,ATDW,,>
VideoBuf_Sel    =       VideoBuf-GDT
;----------------------------------------------------------------------------
EFFGDT          LABEL   BYTE
                ;临时代码段描述符及选择子
TempCode        Desc    <0ffffh,TempCodeSeg,,ATCE,,>
TempCode_Sel    =       TempCode-GDT
                ;演示代码段描述符及选择子
DemoCode        Desc    <DemoCodeLen-1,DemoCodeSeg,,ATCE,,>
DemoCode_Sel    =       DemoCode-GDT
                ;演示任务局部描述符表段描述符及选择子
DemoLDT         Desc    <DemoLDTLen-1,DemoLDTSeg,,ATLDT,,>
DemoLDT_Sel     =       DemoLDT-GDT
                ;演示任务TSS段描述符及选择子
DemoTSS         Desc    <DemoTSSLen-1,DemoTSSSeg,,AT386TSS,,>
DemoTSS_Sel     =       DemoTSS-GDT
                ;缓冲数据段描述符及选择子
XBuffer         Desc    <BufferLen-1,BufferSeg,,ATDW,,>
XBuffer_Sel     =       XBuffer-GDT
                ;读键盘任务局部描述符表段描述符及选择子
GKeyLDT         Desc    <GKeyLDTLen-1,GKeyLDTSeg,,ATLDT,,>
GKeyLDT_Sel     =       GKeyLDT-GDT
                ;读键盘任务TSS段描述符及选择子
GKeyTSS         Desc    <GKeyTSSLen-1,GKeyTSSSeg,,AT386TSS,,>
GKeyTSS_Sel     =       GKeyTSS-GDT
                ;显示陷阱处理程序代码段描述符及选择子
EchoCode        Desc    <EchoCodeLen-1,EchoCodeSeg,,ATCE,,>
EchoCode_Sel    =       EchoCode-GDT
                ;显示出错码过程代码段描述符及选择子
SubCode         Desc    <SubCodeLen-1,SubCodeSeg,,ATCE,,>
SubCode_Sel     =       SubCode-GDT
                ;其它中断或异常处理程序代码段描述符及选择子
Other           Desc    <OtherCodeLen-1,OtherCodeSeg,,ATCE,,>
Other_Sel       =       Other-GDT
;----------------------------------------------------------------------------
GDTLen          =       $-GDT                     ;全局描述符表长度
GDNum           =       ($-EFFGDT)/(SIZE Desc)    ;需处理基地址的描述符个数
;----------------------------------------------------------------------------
GDTSeg          ENDS                              ;全局描述符表段定义结束
;----------------------------------------------------------------------------
IDTSeg          SEGMENT PARA USE16                ;中断描述符表数据段(16位)
;----------------------------------------------------------------------------
IDT             LABEL   BYTE                      ;中断描述符表
                ;0号陷阱门描述符(对应除法出错故障)
                Gate    <DivBegin,Divide_Sel,,AT386TGate,>
                ;从1--3的3个陷阱门描述符
                REPT    3
                Gate    <OtherBegin,Other_Sel,,AT386TGate,>
                ENDM
                ;4号陷阱门描述符(对应溢出陷阱)
                Gate    <OFBegin,OF_Sel,,AT386TGate,>
                ;从5--0ah的的6个陷阱门描述符
                REPT    6
                Gate    <OtherBegin,Other_Sel,,AT386TGate,>
                ENDM
                ;0bh号陷阱门描述符(对应段不存在故障)
                Gate    <SNPBegin,SNP_Sel,,AT386TGate,>
                ;0ch号陷阱门描述符(对应堆栈段故障)
                Gate    <SSEBegin,SSE_Sel,,AT386TGate,>
                ;0dh号陷阱门描述符(对应通用保护故障)
                Gate    <GPBegin,GP_Sel,,AT386TGate,>
                ;从0eh--0edh的240个陷阱门描述符
                REPT    240
                Gate    <OtherBegin,Other_Sel,,AT386TGate,>
                ENDM
                ;对应0feh号陷阱门描述符(对应显示中断处理程序)
                Gate    <EchoBegin,EchoCode_Sel,,AT386TGate,>
                ;0ffh号任务门描述符(对应读键盘中断处理任务)
                Gate    <,GKeyTSS_Sel,,ATTaskGate,>
;----------------------------------------------------------------------------
IDTLen          =       $-IDT
;----------------------------------------------------------------------------
IDTSeg          ENDS                              ;中断描述符表段定义结束
;----------------------------------------------------------------------------
;读键盘任务局部描述符表段
;----------------------------------------------------------------------------
GKeyLDTSeg      SEGMENT PARA USE16
;----------------------------------------------------------------------------
GLDT            LABEL   BYTE
                ;代码段描述符及选择子
GKeyCode        Desc    <0ffffh,GKeyCodeSeg,,ATCE,,>
GKeyCode_Sel    =       GKeyCode-GLDT+TIL
                ;堆栈段描述符及选择子
GKeyStack       Desc    <GKeyStackLen-1,GKeyStackSeg,,ATDWA,,>
GKeyStack_Sel   =       GKeyStack-GLDT+TIL
;----------------------------------------------------------------------------
GKeyLDNum       =       ($-GLDT)/(SIZE Desc)      ;需初始化基地址的描述符个数
GKeyLDTLen      =       $                         ;局部描述符表段长度
;----------------------------------------------------------------------------
GKeyLDTSeg      ENDS
;----------------------------------------------------------------------------
;读键盘任务TSS段
;----------------------------------------------------------------------------
GKeyTSSSeg      SEGMENT PARA USE16
                DD      0                         ;链接字
                DD      ?                         ;0级堆栈指针
                DW      ?,?
                DD      ?                         ;1级堆栈指针
                DW      ?,?
                DD      ?                         ;2级堆栈指针
                DW      ?,?
                DD      0                         ;CR3
                DW      GKeyBegin,0               ;EIP
                DD      0                         ;EFLAGS
                DD      0                         ;EAX
                DD      0                         ;ECX
                DD      0                         ;EDX
                DD      0                         ;EBX
                DW      GKeyStackLen,0            ;ESP
                DD      0                         ;EBP
                DD      0                         ;ESI
                DD      0                         ;EDI
                DW      Normal_Sel,0              ;ES
                DW      GKeyCode_Sel,0            ;CS
                DW      GKeyStack_Sel,0           ;SS
                DW      Normal_Sel,0              ;DS
                DW      Normal_Sel,0              ;FS
                DW      Normal_Sel,0              ;GS
                DW      GKeyLDT_Sel,0             ;LDTR
                DW      0                         ;调试陷阱标志
                DW      $+2                       ;指向I/O许可位图的偏移
                DB      0ffh                      ;I/O许可位图结束字节
GKeyTSSLen      =       $
GKeyTSSSeg      ENDS
;----------------------------------------------------------------------------
;读键盘任务堆栈段
;----------------------------------------------------------------------------
GKeyStackSeg    SEGMENT PARA USE16
GKeyStackLen    =       1024
                DB      GKeyStackLen DUP(0)
GKeyStackSeg    ENDS
;----------------------------------------------------------------------------
;读键盘任务代码段
;----------------------------------------------------------------------------
GKeyCodeSeg     SEGMENT PARA USE16
                ASSUME  CS:GKeyCodeSeg,DS:RDataSeg,ES:BufferSeg
;----------------------------------------------------------------------------
GKeyBegin       PROC    FAR
                push    ds
                push    es
                push    fs
                push    gs
                mov     ax,Normal_Sel
                mov     ss,ax                     ;准备转实方式
                mov     eax,cr0
                and     al,11111110b
                mov     cr0,eax                   ;转实方式
                JUMP16  <SEG GetKey>,<OFFSET GetKey>
GetKey:         mov     ax,RDataSeg               ;实方式
                mov     ds,ax
                mov     ebp,esp                   ;恢复实方式部分现场
                lss     sp,DWORD PTR SPVar
                lidt    QWORD PTR NORVIDTR
                sti
                mov     dx,OFFSET Mess
                mov     ah,9
                int     21h                       ;显示提示信息
GetKey1:        mov     ah,0
                int     16h                       ;读键盘
                cmp     al,'0'
                jz      GetKey2
                cmp     al,'4'
                jz      GetKey2
                and     al,11011111b              ;小写转大写
                cmp     al,'B'
                jb      GetKey1
                cmp     al,'D'
                ja      GetKey1                   ;只有[0,4,b,c,d]有效
GetKey2:        mov     dl,al
                mov     ah,2
                int     21h                       ;显示所按字符
                mov     ax,BufferSeg
                mov     es,ax
                mov     BYTE PTR es:KeyASCII,dl   ;保存到缓冲数据段
                cli                               ;准备返回保护方式
                lidt    QWORD PTR VIDTR
                mov     eax,cr0
                or      al,1
                mov     cr0,eax
                JUMP16  <GKeyCode_Sel>,<OFFSET GetKeyV>
GetKeyV:        mov     ax,GKeyStack_Sel          ;又进入保护方式
                mov     ss,ax
                mov     esp,ebp
                pop     gs
                pop     fs
                pop     es
                pop     ds
                iretd
                jmp     GKeyBegin
GKeyBegin       ENDP
;----------------------------------------------------------------------------
GKeyCodeLen     =       $
GKeyCodeSeg     ENDS
;----------------------------------------------------------------------------
;其它中断或异常处理程序的代码段
;----------------------------------------------------------------------------
OtherCodeSeg    SEGMENT PARA USE16
                ASSUME  CS:OtherCodeSeg
;----------------------------------------------------------------------------
OtherBegin      PROC    FAR
                mov     si,OFFSET MessOther
                int     0feh                      ;显示提示信息
                mov     WORD PTR es:[0],ax
                jmp     $                         ;进入无限循环
OtherBegin      ENDP
;----------------------------------------------------------------------------
OtherCodeLen    =       $
OtherCodeSeg    ENDS
;----------------------------------------------------------------------------
;除法出错故障处理程序代码段
;----------------------------------------------------------------------------
DivCodeSeg      SEGMENT PARA USE16
                ASSUME  CS:DivCodeSeg
;----------------------------------------------------------------------------
DivBegin        PROC    FAR
                mov     si,OFFSET Mess0
                mov     di,0
                int     0feh                      ;显示提示信息
                shr     ax,1                      ;处理模拟的除法错误
                iretd                             ;返回
DivBegin        ENDP
;----------------------------------------------------------------------------
DivCodeLen      =       $
DivCodeSeg      ENDS
;----------------------------------------------------------------------------
;溢出陷阱处理程序代码段
;----------------------------------------------------------------------------
OFCodeSeg       SEGMENT PARA USE16
                ASSUME  CS:OFCodeSeg
;----------------------------------------------------------------------------
OFBegin         PROC    FAR
                mov     si,OFFSET Mess4
                mov     di,0
                int     0feh                      ;显示提示信息
                iretd                             ;返回
OFBegin         ENDP
;----------------------------------------------------------------------------
OFCodeLen       =       $
OFCodeSeg       ENDS
;----------------------------------------------------------------------------
;段不存在故障处理程序代码段
;----------------------------------------------------------------------------
SNPCodeSeg      SEGMENT PARA USE16
                ASSUME  CS:SNPCodeSeg
;----------------------------------------------------------------------------
SNPBegin        PROC    FAR
                mov     si,OFFSET MessB
                mov     di,0
                int     0feh                      ;显示提示信息
                pop     eax                       ;弹出出错代码
                CALL16  SubCode_Sel,SubBegin      ;显示出错代码
                pop     eax
                add     eax,2                     ;按模拟的引起段不存在指令
                push    eax                       ;调整返回地址
                iretd
SNPBegin        ENDP
;----------------------------------------------------------------------------
SNPCodeLen      =       $
SNPCodeSeg      ENDS
;----------------------------------------------------------------------------
;堆栈段故障处理程序代码段
;----------------------------------------------------------------------------
SSECodeSeg      SEGMENT PARA USE16
                ASSUME  CS:SSECodeSeg
;----------------------------------------------------------------------------
SSEBegin        PROC    FAR
                mov     si,OFFSET MessC
                mov     di,0
                int     0feh                      ;显示提示信息
                pop     eax                       ;弹出出错代码
                CALL16  SubCode_Sel,SubBegin      ;显示出错代码
                pop     eax
                add     eax,4                     ;按模拟的引起堆栈段错误的
                push    eax                       ;指令调整返回地址
                iretd
SSEBegin        ENDP
;----------------------------------------------------------------------------
SSECodeLen      =       $
SSECodeSeg      ENDS
;----------------------------------------------------------------------------
;通用保护故障处理程序代码段
;----------------------------------------------------------------------------
GPCodeSeg       SEGMENT PARA USE16
                ASSUME  CS:GPCodeSeg
;----------------------------------------------------------------------------
GPBegin         PROC    FAR
                push    ebp
                mov     ebp,esp
                push    eax
                push    esi
                push    edi                       ;保护现场
                mov     si,OFFSET MessD
                mov     di,0
                int     0feh                      ;显示提示信息
                mov     eax,[bp+4]                ;从堆栈中取出出错代码
                CALL16  SubCode_Sel,SubBegin      ;显示出错代码
                pop     edi
                pop     esi
                pop     eax                       ;恢复部分现场
                add     DWORD PTR [ebp+8],2       ;按模拟的故障指令调整返回
                pop     ebp                       ;地址
                add     esp,4                     ;废除堆栈中的出错代码
                iretd
GPBegin         ENDP
;----------------------------------------------------------------------------
;显示出错码过程代码段
;----------------------------------------------------------------------------
SubCodeSeg      SEGMENT PARA USE16
                ASSUME  CS:SubCodeSeG
;----------------------------------------------------------------------------
SubBegin        PROC                              ;AX中含出错代码
                push    ax                        ;保护现场
                push    cx
                push    dx
                push    si
                push    di
                mov     si,OFFSET ErrCode
                mov     dx,ax
                mov     cx,4
SubR1:          rol     dx,4                      ;把16位出错代码转换成4位
                mov     al,dl                     ;十六进制数的ASCII码并保存
                and     al,0fh
                add     al,30h
                cmp     al,'9'
                jbe     SubR2
                add     al,7
SubR2:          mov     [si],al
                inc     si
                loop    SubR1
                mov     si,OFFSET ErrMess
                Mov     di,80*2                   ;从第二行行首开始
                int     0feh                      ;显示出错码
                pop     di                        ;恢复现场
                pop     si
                pop     dx
                pop     cx
                pop     ax
                retf                              ;返回
SubBegin        ENDP
;----------------------------------------------------------------------------
SubCodeLen      =       $
SubCodeSeg      ENDS
;----------------------------------------------------------------------------
GPCodeLen       =       $
GPCodeSeg       ENDS
;----------------------------------------------------------------------------
;实现显示的陷阱处理程序代码段
;入口参数--DS:SI指向显示信息串,ES:DI指向显示缓冲区
;----------------------------------------------------------------------------
EchoCodeSeg     SEGMENT PARA USE16
                ASSUME  CS:EchoCodeSeg
;----------------------------------------------------------------------------
EchoBegin       PROC    FAR
                pushad                            ;保护现场
                cld
                mov     ah,7
                mov     al,20h
                mov     cx,80
                push    di
                rep     stosw                     ;清所在显示行
                pop     di
Echo1:          lodsb
                or      al,al
                jz      Echo2
                stosw                             ;显示指定信息串
                jmp     Echo1
Echo2:          popad                             ;恢复现场
                iretd
EchoBegin       ENDP
;----------------------------------------------------------------------------
EchoCodeLen     =       $
EchoCodeSeg     ENDS
;----------------------------------------------------------------------------
;缓冲区数据段
;----------------------------------------------------------------------------
BufferSeg       SEGMENT PARA USE16
KeyASCII        DB      ?
Buffer          DB      128 DUP(?)
BufferLen       =       $
BufferSeg       ENDS
;----------------------------------------------------------------------------
;演示任务局部描述符表段
;----------------------------------------------------------------------------
DemoLDTSeg      SEGMENT PARA USE16
;----------------------------------------------------------------------------
DLDT            LABEL   BYTE
                ;演示任务TSS段作为数据段的描述符及选择子
ToDemoTSS       Desc    <DemoTSSLen-1,DemoTSSSeg,,ATDW,,>
ToDemoTSS_Sel   =       ToDemoTSS-DLDT+TIL
                ;演示任务堆栈段描述符及选择子
DemoStack       Desc    <DemoStackLen-1,DemoStackSeg,,ATDWA,,>
DemoStack_Sel   =       DemoStack-DLDT+TIL
                ;演示任务数据段描述符及选择子
DemoData        Desc    <DemoDataLen-1,DemoDataSeg,,ATDW,,>
DemoData_Sel    =       DemoData-DLDT+TIL
                ;除法出错故障处理程序代码段描述符及选择子
Divide          Desc    <DivCodeLen-1,DivCodeSeg,,ATCE,,>
Divide_Sel      =       Divide-DLDT+TIL
                ;溢出陷阱处理程序代码段描述符及选择子
OverFlow        Desc    <OFCodeLen-1,OFCodeSeg,,ATCE,,>
OF_Sel          =       OverFlow-DLDT+TIL
                ;段不存在故障处理程序代码段描述符及选择子
SNPCode         Desc    <SNPCodeLen-1,SNPCodeSeg,,ATCE,,>
SNP_Sel         =       SNPCode-DLDT+TIL
                ;堆栈段出错故障处理程序代码段描述符及选择子
SSECode         Desc    <SSECodeLen-1,SSECodeSeg,,ATCE,,>
SSE_Sel         =       SSECode-DLDT+TIL
                ;通用保护故障处理程序代码段描述符及选择子
GPCode          Desc    <GPCodeLen-1,GPCodeSeg,,ATCE,,>
GP_Sel          =       GPCode-DLDT+TIL
                ;为模拟段不存在故障而安排的数据段描述符及选择子
TestNPS         Desc    <0ffffh,,,ATDW-80h,,>
TestNPS_Sel     =       TestNPS-DLDT+TIL
;----------------------------------------------------------------------------
DemoLDNum       =       ($-DLDT)/(SIZE Desc)      ;LDT描述符个数
DemoLDTLen      =$
;----------------------------------------------------------------------------
DemoLDTSeg      ENDS
;----------------------------------------------------------------------------
;演示任务TSS段
;----------------------------------------------------------------------------
DemoTSSSeg      SEGMENT PARA USE16
DemoTaskSS      TSS     <>
                DB      0ffh
DemoTSSLen      =       $
DemoTSSSeg      ENDS
;----------------------------------------------------------------------------
;演示任务的堆栈段
;----------------------------------------------------------------------------
DemoStackSeg    SEGMENT PARA USE16
DemoStackLen    =       1024
                DB      DemoStackLen DUP(0)
DemoStackSeg    ENDS
;----------------------------------------------------------------------------
;演示任务的数据段
;----------------------------------------------------------------------------
DemoDataSeg     SEGMENT PARA USE16
Mess0           DB      'Divide Error (Exception 0)',0
Mess4           DB      'Overflow (Exception 4)',0
MessB           DB      'Segment Not Present (Exception 11)',0
MessC           DB      'Stack Segment (Exception 12)',0
MessD           DB      'General Protection (Exception 13)',0
MessOther       DB      'Other Exception',0
ErrMess         DB      'Error Code = '
ErrCode         DB      4 DUP(0),'H',0
DemoDataLen     =       $
DemoDataSeg     ENDS
;----------------------------------------------------------------------------
;演示任务的代码段
;----------------------------------------------------------------------------
DemoCodeSeg     SEGMENT PARA USE16
                ASSUME  CS:DemoCodeSeg,DS:DemoDataSeg
;----------------------------------------------------------------------------
DemoBegin       PROC    FAR
                mov     ax,DemoLDT_Sel
                lldt    ax                        ;装载LDTR
                mov     ax,DemoStack_Sel          ;置堆栈
                mov     ss,ax
                mov     esp,DemoStackLen
                mov     ax,ToDemoTSS_Sel
                mov     gs,ax                     ;把演示任务LDT选择子填入TSS
                mov     WORD PTR gs:DemoTaskSS.TRLDTR,DemoLDT_Sel
                mov     ax,DemoTSS_Sel
                ltr     ax                        ;装载TR
                mov     ax,DemoData_Sel
                mov     ds,ax                     ;装载其它数据段寄存器
                mov     ax,VideoBuf_Sel
                mov     es,ax
                mov     ax,XBuffer_Sel
                mov     fs,ax
                mov     ax,XBuffer_Sel
                mov     gs,ax
                int     0ffh                      ;接收要模拟的异常类型号
                mov     al,BYTE PTR fs:KeyASCII   ;按接收的字符模拟异常号
                cmp     al,'0'
                jnz     Demo4
                mov     ax,2000
                mov     cl,2                      ;模拟除法出错故障
                div     cl                        ;该指令长2字节
                jmp     Over
Demo4:          cmp     al,'4'
                jnz     Demo11
                mov     al,100
                add     al,50
                into                              ;模拟溢出陷阱
                JMP     OVER
Demo11:         cmp     al,'B'
                jnz     Demo12
                mov     ax,TestNPS_Sel            ;模拟段不存在故障
                mov     gs,ax                     ;该指令长2字节
                JMP     Over
Demo12:         cmp     al,'C'
                jnz     Demo13
                mov     ebp,esp                   ;模拟堆栈出错故障
                mov     al,[ebp]                  ;该指令长4字节
                jmp     Over
Demo13:         mov     ax,DemoTSS_Sel            ;模拟通用保护故障
                mov     gs,ax                     ;该指令长2字节
Over:           ;转临时代码段
                JUMP16  TempCode_Sel,<OFFSET ToDos>
DemoBegin       ENDP
;----------------------------------------------------------------------------
DemoCodeLen     =       $
DemoCodeSeg     ENDS
;----------------------------------------------------------------------------
TempCodeSeg     SEGMENT PARA USE16                ;临时任务的代码段
                ASSUME  CS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual         PROC    FAR
                JUMP16  DemoCode_Sel,DemoBegin    ;转演示任务
ToDos:          mov     ax,Normal_Sel             ;恢复实方式段描述符高速缓存
                mov     ds,ax
                mov     es,ax
                mov     fs,ax
                mov     gs,ax
                mov     ss,ax
                mov     eax,cr0                   ;准备返回实模式
                and     al,11111110b
                mov     cr0,eax
                JUMP16  <SEG Real>,<OFFSET Real>
Virtual         ENDP
;----------------------------------------------------------------------------
TempCodeSeg     ENDS
;============================================================================
RDataSeg        SEGMENT PARA USE16                ;实方式数据段
VGDTR           PDesc   <GDTLen-1,>               ;GDT伪描述符
VIDTR           PDesc   <IDTLen-1,>               ;IDT伪描述符
NORVIDTR        PDesc   <3ffh,>                   ;用于保存原IDTR值
SPVar           DW      ?                         ;用于保存实方式下的SP
SSVar           DW      ?                         ;用于保存实方式下的SS
Mess            DB      'Press a key[0,4,B,C,D]:$';提示信息
RDataSeg        ENDS
;----------------------------------------------------------------------------
RCodeSeg        SEGMENT PARA USE16                ;实方式代码段
                ASSUME  CS:RCodeSeg,DS:RDataSeg
;----------------------------------------------------------------------------
Start           PROC
                mov     ax,RDataSeg
                mov     ds,ax
                cld
                call    InitGDT                   ;初始化全局描述符表GDT
                call    InitIDT                   ;初始化中断描述符表IDT
                mov     ax,GKeyLDTSeg
                mov     fs,ax
                mov     cx,GKeyLDNum
                mov     si,OFFSET GLDT
                CALL    InitLDT
                mov     ax,DemoLDTSeg
                mov     fs,ax
                mov     cx,DemoLDNum
                mov     si,OFFSET DLDT
                CALL    InitLDT
                mov     SSVar,ss                  ;保存堆栈指针
                mov     SPVar,sp
                lgdt    QWORD PTR VGDTR           ;装载GDTR
                sidt    QWORD PTR NORVIDTR        ;保存IDTR
                cli                               ;关中断
                lidt    QWORD PTR VIDTR           ;装载IDTR
                mov     eax,cr0
                or      al,1
                mov     cr0,eax
                JUMP16  <TempCode_Sel>,<OFFSET Virtual>
Real:           mov     ax,RDataSeg
                mov     ds,ax
                lss     sp,DWORD PTR SPVar        ;又回到实方式
                lidt    QWORD PTR NORVIDTR
                sti
                mov     ax,4c00h
                int     21h
Start           ENDP
;----------------------------------------------------------------------------
InitGDT         PROC
                push    ds
                mov     ax,GDTSeg
                mov     ds,ax
                mov     cx,GDNum
                mov     si,OFFSET EFFGDT
InitG:          mov     ax,[si].BaseL
                movzx   eax,ax
                shl     eax,4
                shld    edx,eax,16
                mov     WORD PTR [si].BaseL,ax
                mov     BYTE PTR [si].BaseM,dl
                mov     BYTE PTR [si].BaseH,dh
                add     si,SIZE Desc
                loop    InitG
                pop     ds
                mov     bx,16
                mov     ax,GDTSeg
                mul     bx
                mov     WORD PTR VGDTR.Base,ax
                mov     WORD PTR VGDTR.Base+2,dx
                ret
InitGDT         ENDP
;----------------------------------------------------------------------------
InitIDT         PROC
                mov     bx,16
                mov     ax,IDTSeg
                mul     bx
                mov     WORD PTR VIDTR.Base,ax
                mov     WORD PTR VIDTR.Base+2,dx
                ret
InitIDT         ENDP
;----------------------------------------------------------------------------
;入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数
;----------------------------------------------------------------------------
InitLDT         PROC
ILDT:           mov     ax,WORD PTR FS:[si].BaseL
                movzx   eax,ax
                shl     eax,4
                shld    edx,eax,16
                mov     WORD PTR fs:[si].BaseL,ax
                mov     BYTE PTR fs:[si].BaseM,dl
                mov     BYTE PTR fs:[si].BaseH,dh
                add     si,SIZE Desc
                loop    ILDT
                ret
InitLDT         ENDP
;----------------------------------------------------------------------------
RCodeSeg        ENDS
                END     Start



| 参考资料 | 书 名 | 出 版 社 | 作 者 | 
| 《保护方式下的80386及其编程》 | 清华大学出版社 | 周明德主编 | |
| 《80X86汇编语言程序设计教程》 | 清华大学出版社 | 扬季文主编 | |
| 《32位系统软件编程指南》 | 电子工业出版社 | 程荷、武航翻译 |